#if !defined(windows_x86)
#include "plc.h"
#include "hwboard.h"
#include "iec_types.h"
#ifdef ARDUINO
#include <Arduino.h>
#include <STM32FreeRTOS.h>
#include "semphr.h"
#include "timers.h"
#endif

extern IEC_BOOL __DEBUG;

#ifdef ONLINE_DEBUG

static unsigned long __debug_tick;
#endif
#ifdef ARDUINO
osThreadId PLC_thread;
osSemaphoreDef(RSEM);
//osSemaphoreId debug_wait_mutex;
#ifdef ONLINE_DEBUG
osSemaphoreId debug_mutex;
osSemaphoreDef(DSEM);
osSemaphoreDef(WSEM);
#endif
#else
pthread_t PLC_thread;
static pthread_mutex_t debug_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
char PLC_shutdown = 0;
uint8_t reboot = 0;
boolean POSTFILE = 0, POSTED = 0;
int ForceSaveRetainReq(void)
{
        return PLC_shutdown;
}
extern void __run(void);
static void PLC_thread_proc(void const *arg)
{
        // __init(0, 0);
        uint32_t tmo = millis() + common_ticktime__ / 1000000;
        while (1)
        {
                if (millis() >= tmo)
                {
                        tmo = millis() + common_ticktime__ / 1000000;
                        PLC_GetTime(&__CURRENT_TIME);
                        __CURRENT_TIME = __CURRENT_TIME;
                        if (!PLC_shutdown)
                        {
                                plc_set_state( Started);

                                plc_run_start();
                                __run();
                                // __publish_debug();
                                plc_run_end();
                        }
                        else
                        {
                                plc_set_state(Stopped);
                        }
                }
                else
                        vTaskDelay(1);
                if (reboot)
                {
                        vTaskDelay(1000);
                        HAL_NVIC_SystemReset();
                }
                while (POSTFILE)
                {
                        // void plc_postfile();
                        // plc_postfile();
                        POSTED = 1;
                        if (reboot)
                        {
                                HAL_NVIC_SystemReset();
                        }
                }
        }
#ifndef ARDUINO
        __cleanup_debug();
        __cleanup();
#ifdef ARDUINO
#ifdef ONLINE_DEBUG

        osSemaphoreRelease(debug_mutex);
        //        vSemaphoreDelete(debug_wait_mutex);
        vSemaphoreDelete(debug_mutex);
#endif
#else
        pthread_mutex_destroy(&debug_wait_mutex);
        pthread_mutex_destroy(&debug_mutex);
#endif
#ifdef ARDUINO
        vTaskDelete(NULL);
#else
        pthread_exit(0);
#endif
#endif
}
#define maxval(a, b) ((a > b) ? a : b)
extern "C"
{
        long AtomicCompareExchange(long *atomicvar, long compared, long exchange)
        {
                return 0; //__sync_val_compare_and_swap(atomicvar, compared, exchange);
        }
        long long AtomicCompareExchange64(long long *atomicvar, long long compared, long long exchange)
        {
                return 0; //__sync_val_compare_and_swap(atomicvar, compared, exchange);
        }
        void PLC_GetTime(IEC_TIME *CURRENT_TIME)
        {
                // struct timespec tmp;
                // clock_gettime(CLOCK_REALTIME, &tmp);
                uint32_t t = millis();
                uint32_t sec = t / 1000;
                uint32_t ms = t % 1000;
                CURRENT_TIME->tv_sec = sec;
                CURRENT_TIME->tv_nsec = ms * 1000000;
        }

        int __init();
        int startPLC()
        {
                // struct sigevent sigev;
                // setlocale(LC_NUMERIC, "C");
                __init();
                // __init_debug();
                PLC_shutdown = 0;
#ifdef ARDUINO
                osThreadDef(plc_thd, &PLC_thread_proc, osPriorityNormal, 0, 512);
                PLC_thread = osThreadCreate(osThread(plc_thd), NULL);
#else
                pthread_create(&PLC_thread, NULL, (void *)&PLC_thread_proc, NULL);
#endif

#ifdef ARDUINO

//        debug_wait_mutex = osSemaphoreCreate(osSemaphore(DSEM), 1);
#ifdef ONLINE_DEBUG

                debug_mutex = osSemaphoreCreate(osSemaphore(WSEM), 1);
#endif
                //        osSemaphoreWait(debug_wait_mutex, -1);
#else
                pthread_mutex_init(&debug_wait_mutex, NULL);
                pthread_mutex_init(&debug_mutex, NULL);

                pthread_mutex_lock(&debug_wait_mutex);
#endif

                return 0;
        }
#ifdef ONLINE_DEBUG
        unsigned long lastDebugTick;

        int TryEnterDebugSection(void)
        {

#ifdef ARDUINO
                if (osSemaphoreWait(debug_mutex, 0xffffffff) == osOK)
#else
                if (pthread_mutex_trylock(&debug_mutex) == 0)
#endif
                {
                        /* Only enter if debug active */
                        if (__DEBUG)
                        {
                                return 1;
                        }
#ifdef ARDUINO
                        osSemaphoreRelease(debug_mutex);
#else
                        pthread_mutex_unlock(&debug_mutex);
#endif
                }
                return 0;
        }

        void LeaveDebugSection(void)
        {
#ifdef ARDUINO
                osSemaphoreRelease(debug_mutex);
#else
                pthread_mutex_unlock(&debug_mutex);
#endif
                if (__DEBUG && millis() - lastDebugTick > 100000)
                {
                        suspendDebug(1);
                }
        }
#endif
        void __cleanup(void);
        int stopPLC()
        {
                /* Stop the PLC */

                PLC_shutdown = 1;
#ifdef ARDUINO
                // vTaskDelete(PLC_thread);

#else
                pthread_join(PLC_thread, NULL);
#endif
                ResetDebugVariables();

                return 0;
        }
        void runPLC()
        {
#ifdef ONLINE_DEBUG
                ResetDebugVariables();
#endif
                PLC_shutdown = 0;
        }

#ifdef ONLINE_DEBUG

        void RegisterDebugVariable(unsigned int idx, void *force);
        int WaitDebugData(unsigned long *tick)
        {
                int res = 0;
                lastDebugTick = millis();
                if (PLC_shutdown)
                        return 1;
/* Wait signal from PLC thread */
#ifdef ARDUINO
                res = osSemaphoreWait(debug_mutex, 100);
#else
                res = pthread_mutex_lock(&debug_wait_mutex);
#endif
                if (res == osOK)
                {
                        *tick = __debug_tick;
                }
                return res;
        }
        int endDebugData()
        {
                osSemaphoreRelease(debug_mutex);
                return 0;
        }
        extern unsigned long __tick;
        void InitiateDebugTransfer()
        {
                /* remember tick */
                __debug_tick = __tick;
                /* signal debugger thread it can read data */
#ifdef ARDUINO
//        osSemaphoreRelease(debug_wait_mutex);
#else
                pthread_mutex_unlock(&debug_wait_mutex);
#endif
        }
        void suspendDebug(int disable)
        {
                /* Prevent PLC to enter debug code */
#ifdef ARDUINO
                osSemaphoreRelease(debug_mutex);
#else
                pthread_mutex_unlock(&debug_mutex);
#endif /*__DEBUG is protected by this mutex */
                __DEBUG = !disable;
                ResetDebugVariables();
                if (disable)
#ifdef ARDUINO
                        osSemaphoreRelease(debug_mutex);
#else
                        pthread_mutex_unlock(&debug_mutex);
#endif
        }

        void resumeDebug(void)
        {
                __DEBUG = 1;
/* Let PLC enter debug code */
#ifdef ARDUINO
                osSemaphoreRelease(debug_mutex);
#else
                pthread_mutex_unlock(&debug_mutex);
#endif
        }
#endif
#include <sys/time.h>

        int _gettimeofday(struct timeval *tv, void *tzvp)
        {
                uint64_t t = millis();                 // get uptime in nanoseconds
                tv->tv_sec = t / 1000000000;           // convert to seconds
                tv->tv_usec = (t % 1000000000) / 1000; // get remaining microseconds
                return 0;                              // return non-zero for error
        }                                              // end _gettimeofday()
}
void setPLCShutdown()
{
        PLC_shutdown = 1;
}
#endif